package com.tool.shiro.service.impl;

import com.tool.shiro.annotation.ApiInfoAnnotation;
import com.tool.shiro.config.DBConfig;
import com.tool.shiro.dao.ShiroAdminDao;
import com.tool.shiro.mapper.basic.*;
import com.tool.shiro.mapper.ShiroUserMapper;
import com.tool.shiro.pojo.*;
import com.tool.shiro.service.ShiroResourceService;
import com.tool.shiro.service.ShiroAdminService;
import com.tool.shiro.util.CommonUtils;
import com.tool.shiro.util.SaltUtils;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.annotation.Resource;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.*;

@Service
@Transactional
public class ShiroAdminServiceImpl implements ShiroAdminService {

    @Autowired
    private ShiroAdminDao shiroAdminDao;

    @Autowired
    private ShiroResourceService shiroResourceService;

    @Resource
    private ShiroUserMapper shiroUserMapper;

    @Resource
    private ShiroUserRoleMapper shiroUserRoleMapper;

    @Resource
    private ShiroRoleMapper shiroRoleMapper;

    @Resource
    private ShiroRolePermissionMapper shiroRolePermissionMapper;

    @Resource
    private ShiroPermissionMapper shiroPermissionMapper;

    @Override
    public ShiroAdminManage findByAccount(String account) {
        return shiroAdminDao.findByAccount("'" + account + "'");
    }

    @Override
    public boolean findAccountIsExist(String account) {
        return shiroAdminDao.findAccountIsExist(account);
    }

    @Override
    public boolean insertShiroAdmin(ShiroAdminManage shiroAdmin) {
        // 生成随机盐
        String salt = SaltUtils.getSalt(16);
        // 保存随机盐到管理员对象中
        shiroAdmin.setSalt(salt);
        // 将明文密码进行 md5 + salt(盐) + hash散列 加密
        Md5Hash md5Hash = new Md5Hash(shiroAdmin.getPassword(), salt, 1024);
        // 将密码设置为加密后的密文
        shiroAdmin.setPassword(md5Hash.toHex());
        // 保存到数据库并返回
        return shiroAdminDao.insertShiroAdmin(shiroAdmin) == 1;
    }

    @Override
    public Map<String, Object> getDataBaseInfo(){
        try {
            return shiroAdminDao.getDataBaseInfo();
        }catch (Exception e) {
            return null;
        }
    }

    /**
     * 获取spring上下文
     */
    @Autowired
    private ApplicationContext applicationContext;

    @Override
    public boolean updateShiroResource() {
        List<ShiroResource> shiroResourceList = new ArrayList<>();
        try {
            // 获取所有RequestMapping注解接口信息
            List<Map<String, Object>> requestMappingList = CommonUtils.getAllRequestMappingApiInfo();
            // 获取自定义注解(ApiInfoAnnotation)接口信息
            List<Map<String, Object>> apiInfoAnnotationList = new ArrayList<>();
            Map<String, Object> controllers = applicationContext.getBeansWithAnnotation(RequestMapping.class);
            controllers.remove("basicErrorController");         // 排除指定类注解
            for (Map.Entry<String, Object> entry : controllers.entrySet()) {        // 遍历扫描所有包下自定义注解
                // 要扫描的包名
                String packageName = entry.getValue().toString().split("@")[0];
                Map<String, Object> result = CommonUtils.getAnnotationInfo(ApiInfoAnnotation.class, "description", packageName);
                for (Map.Entry<String, Object> entry1 : result.entrySet()) {
                    Map<String, Object> map1 = new HashMap<>();
                    map1.put("class", entry1.getKey());
                    map1.put("description", entry1.getValue());
                    apiInfoAnnotationList.add(map1);
                }
            }
            // 将 ApiInfoAnnotation 注解和 RequestMapping 注解同类项合并
            for (Map<String, Object> map1 : CommonUtils.mergeListBySpecifyField(apiInfoAnnotationList, requestMappingList, "class")) {
                ShiroResource shiroResource = new ShiroResource();
                shiroResource.setMethod((String) map1.get("method"));
                shiroResource.setValue((String) map1.get("url"));
                shiroResource.setType("url");
                shiroResource.setDescription((String) map1.get("description"));
                shiroResource.setControl("anon");
                shiroResourceList.add(shiroResource);
            }
            // TODO: 后续根据需要将删除后添加修改为批量修改，相同value的资源只需修改其他参数，id和value不变，以便角色-资源表对应
            shiroResourceService.deleteShiroResourcesByType("url");
            shiroResourceService.InitializationShiroResource(shiroResourceList);
        }catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    @Qualifier("securityManager")
    private DefaultFilterChainManager defaultFilterChainManager;

    // 注: 刷新shiro权限配置的Bean，必须使用@Lazy注解标记来延迟注入，否则会依赖循环报错，
    @Lazy
    @Resource
    private ShiroFilterFactoryBean shiroFilterFactoryBean;

    @Override
    public boolean resetShiroResourceByDB() {
        //ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //shiroFilterFactoryBean.setSecurityManager((org.apache.shiro.mgt.SecurityManager) defaultFilterChainManager);
        synchronized (shiroFilterFactoryBean) {
            AbstractShiroFilter shiroFilter;
            try {
                shiroFilter = (AbstractShiroFilter) shiroFilterFactoryBean.getObject();
            } catch (Exception e) {
                throw new RuntimeException("get ShiroFilter from shiroFilterFactoryBean error!");
            }
            PathMatchingFilterChainResolver filterChainResolver = (PathMatchingFilterChainResolver) shiroFilter.getFilterChainResolver();
            DefaultFilterChainManager manager = (DefaultFilterChainManager) filterChainResolver.getFilterChainManager();
            // 清空旧权限控制信息
            manager.getFilterChains().clear();
            shiroFilterFactoryBean.getFilterChainDefinitionMap().clear();
            shiroFilterFactoryBean.setFilterChainDefinitionMap(loadFilterChainDefinitions());
            // 重新构建权限控制
            Map<String, String> chains = shiroFilterFactoryBean.getFilterChainDefinitionMap();
            for (Map.Entry<String, String> entry : chains.entrySet()) {
                String url = entry.getKey();
                String chainDefinition = entry.getValue().trim().replace(" ", "");
                manager.createChain(url, chainDefinition);
            }
            return true;
        }
    }

    /**
     * 重新配置权限
     * anon: 可以匿名访问
     * authc: 需认证后访问
     * authBasic:
     * logout:
     * noSessionCreation:
     * perms:
     * port:
     * rest:
     * roles:
     * ssl:
     * user:
     */
    public LinkedHashMap<String, String> loadFilterChainDefinitions() {
        // 权限控制map.从数据库获取或自己添加等
        List<ShiroResource> shiroResourceList = shiroResourceService.getAllShiroResourceInfo();
        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        for (ShiroResource shiroResource : shiroResourceList) {
            if (shiroResource.getControl().equals("anon")) {
                filterChainDefinitionMap.put(shiroResource.getValue(), shiroResource.getControl());
            }
        }
        // anon权限控制写到这
        filterChainDefinitionMap.put("/druid/**", "anon");
        for (ShiroResource shiroResource : shiroResourceList) {
            if (!shiroResource.getControl().equals("anon")) {
                filterChainDefinitionMap.put(shiroResource.getValue(), shiroResource.getControl());
            }
        }
        // authc等权限控制写到这
        filterChainDefinitionMap.put("/admin/updateShiroResource", "authc");
        filterChainDefinitionMap.put("/**", "authc");
        // 默认认证[登录]页面
        shiroFilterFactoryBean.setLoginUrl("/loginPage");
        return filterChainDefinitionMap;
    }

    @Resource
    private ShiroAdminMapper shiroAdminMapper;

    @Override
    public List<ShiroResource> getAllShiroResources() {
        System.out.println(shiroAdminMapper.selectAllShiroResources());
        return shiroAdminMapper.selectAllShiroResources();
    }

    @Override
    public boolean updateShiroResource(ShiroResource shiroResource) {
        return shiroAdminMapper.updateShiroResource(shiroResource) >= 1;
    }

    @Autowired
    DBConfig dbConfig;

    @Override
    public boolean existsTables(List<String> tableNames) throws SQLException {
        Connection connection = null;
        try {
            Class.forName(dbConfig.getDriverClassName());
            connection = DriverManager.getConnection(dbConfig.getUrl(), dbConfig.getUsername(), dbConfig.getPassword());
            if (connection.isClosed()) {
                System.out.println("连接失败");
            }
            return shiroAdminMapper.existTables(connection.getCatalog(), tableNames) == tableNames.size();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }finally {
            connection.close();
        }
        return false;
    }

    @Override
    public List<ShiroRole> queryRolesByUser(ShiroAdminManage user) {
        // 获取shiro工具管理员角色id
        List<Integer> roleIdList = shiroUserRoleMapper.queryUserRolesId(user.getId(), "shiro_admin_manage");
        // 获取用户所有角色对象
        List<ShiroRole> shiroRoles = shiroRoleMapper.queryRoleByRolesId(roleIdList);
        return shiroRoles;
    }

    @Override
    public List<ShiroPermission> queryPermissionsByUser(ShiroAdminManage user) {
        // 获取用户对应角色列表id
        List<Integer> roleIdList = shiroUserRoleMapper.queryUserRolesId(user.getId(), "shiro_admin_manage");
        // 获取角色对应权限列表id
        List<Integer> permissionIdList = shiroRolePermissionMapper.queryRolePermissionsId(roleIdList);
        // 获取用户所有权限对象
        List<ShiroPermission> shiroPermissions = shiroPermissionMapper.queryPermissionsByIds(permissionIdList);
        return shiroPermissions;
    }

    @Override
    public boolean deleteShiroAdminById(ShiroAdminManage shiroAdminManage) {
        return shiroAdminMapper.deleteShiroAdminById(shiroAdminManage) == 1;
    }

    @Override
    public boolean updateShiroAdmin(ShiroAdminManage shiroAdminManage) {
        String salt = SaltUtils.getSalt(16);        // 获取随机盐
        shiroAdminManage.setSalt(salt);
        // TODO: 哈希散列次数后面看需求和场景根据application配置文件或config表动态控制
        Md5Hash md5Hash = new Md5Hash(shiroAdminManage.getPassword(), salt, 1024);
        shiroAdminManage.setPassword(md5Hash.toHex());
        return shiroAdminMapper.updateShiroAdmin(shiroAdminManage) == 1;
    }


}
